<!--
 * @Description: ------------ fileDescription -----------
 * @Author: snows_l snows_l@163.com
 * @Date: 2024-08-08 11:01:12
 * @LastEditors: snows_l snows_l@163.com
 * @LastEditTime: 2025-11-05 17:08:15
 * @FilePath: \BLOG\src\views\article\index.vue
-->
<template>
  <div class="article-wrap">
    <PageTopCover
      :moduleTitle="state.currentModule.moduleTitle"
      :icon="state.currentModule.moduleIcon"
      :textIcon="state.currentModule.moduleTextIcon"
      :imgIcon="state.currentModule.moduleImgIcon"
      :coverImg="coverImg"
      :isMobile="isMobi"
      :modulDesc="state.currentModule.moduleDesc"
      :isArticle="false"></PageTopCover>
    <div class="article-container-wrap" :class="{ 'm-article-container-wrap': isMobi }">
      <div class="article-container-inner" :class="{ 'no-content': state.articleList.length <= 0, 'm-no-content': isMobi && state.articleList.length <= 0 }">
        <!-- 文章列表 -->
        <ArticleList
          type="list"
          :articleList="state.articleList"
          :loading="state.loading"
          :isMobi="isMobi"
          :articleTypeList="state.articleTypeList"
          :colorMap="colorMap"
          :avatar="state.avatar"
          :page="state.page"
          @article-click="handleArticle"
          @view-click="handleView"
          @type-click="handleType"
          @category-click="handleCategory" />
      </div>

      <LoadingMore
        v-if="store.projectConfig.pageType == 'more'"
        :loading="state.loading"
        :hasMore="state.isMore"
        :length="state.articleList.length"
        @loadMore="handleLoadMore"></LoadingMore>
      <CustomPage v-else :page="state.page" @change="handlePageChange" :loading="state.loading" :scorllTop="'article-container-wrap'"></CustomPage>
    </div>
  </div>
</template>

<script lang="ts" setup>
import { getArticleList } from '@/api/article';
import { getDict } from '@/api/common';
import { useAppStore } from '@/store/app';
import { getOnlineImgUrl, getQQAvatar } from '@/utils/common';
import { debounce, throttle } from '@/utils/pref.ts';
import { live2dReloadElement, live2dShowMessage } from '@/utils/live2d';
import { getRandomColor } from '@/utils/theme';
import { onMounted, onUpdated, reactive, watch } from 'vue';
import { useRoute, useRouter } from 'vue-router';
import moment from 'moment';
import ArticleList from '@/components/ArticleList/index.vue';
import useIntersectionObserver from '@/hooks/useIntersectionObserver';
import useResize from '@/hooks/useResize';

const { isMobi } = useResize();
useIntersectionObserver();

const store = useAppStore();
const router = useRouter();
const route = useRoute();
const coverImg = getOnlineImgUrl('185');

type ArticleType = '1' | '2' | '3' | '4';

const typeMap = {
  '1': {
    moduleTitle: '前端',
    moduleTextIcon: '📕',
    // muduleCover: getOnlineImgUrl('117'),
    moduleDesc: ''
  },
  '2': {
    moduleTitle: '后端',
    moduleImgIcon: 'Nodejs.png',
    muduleCover: getOnlineImgUrl('185'),
    moduleDesc: ''
  },
  '3': {
    moduleTitle: '其他分享',
    moduleTextIcon: '📘',
    // muduleCover: getOnlineImgUrl('126'),
    moduleDesc: ''
  },
  '4': {
    moduleTitle: route.query.category,
    moduleTextIcon: '📗',
    // muduleCover: getOnlineImgUrl('31'),
    moduleDesc: ''
  }
};

const colorMap = {
  0: getRandomColor(),
  1: getRandomColor(),
  2: getRandomColor(),
  3: getRandomColor(),
  4: getRandomColor(),
  5: getRandomColor(),
  6: getRandomColor(),
  7: getRandomColor(),
  8: getRandomColor(),
  9: getRandomColor()
};

type TypeMap = typeof typeMap;

const state = reactive({
  avatar: getQQAvatar(),
  loading: false,
  isMore: true,
  page: {
    page: 1,
    size: 10,
    total: 0
  },
  articleList: [],
  articleTypeList: [],
  currentModule: route.query.type ? typeMap[route.query.type as ArticleType] : typeMap['1']
});

interface ListParams {
  page: number;
  size: number;
  [key: string]: any;
}

// 使用节流优化路由跳转
const throttledRouterPush = throttle((path: string, query: any) => {
  router.push({ path, query });
}, 300);

// 获取文章列表
const getArticleListFn = async (scorllTopCallback?: Function): void => {
  try {
    state.loading = true;
    const params: ListParams = { ...state.page };
    if (route.query.type == '4') {
      params.label = route.query.category;
    } else {
      params.type = route.query.type;
    }
    const res = await getArticleList(params);
    if (res.code == 200) {
      res.data.forEach(item => {
        item.createTime = moment(item.createTime).format('YYYY-MM-DD HH:mm:ss');
        item.cover = import.meta.env.VITE_CURRENT_ENV == 'dev' ? import.meta.env.VITE_DEV_BASE_SERVER + item.cover : store.projectConfig.bsServerUrl + item.cover;
        item.labels = item.labels ? item.labels.split(',') : [];
      });
      if (store.projectConfig.pageType == 'more') {
        state.articleList = [...state.articleList, ...res.data];
      } else {
        state.articleList = res.data;
      }
      state.page.total = res.total;
      if (state.articleList.length >= res.total) {
        state.isMore = false; // 已经没有更多数据了
      }
      scorllTopCallback && scorllTopCallback();
    }
  } catch (error) {
  } finally {
    state.loading = false;
  }
};

// 使用防抖优化加载更多
const debouncedGetArticleList = debounce(getArticleListFn, 300);

// 获取文章类型
const getArticleTypeList = () => {
  getDict({ dictType: 'article_type' }).then(res => {
    state.articleTypeList = res.data;
    store.SET_ARTICLE_DICT(res.data);
  });
};

store.articleDict.length > 0 ? (state.articleTypeList = store.articleDict) : getArticleTypeList();

// 加载更多 - 使用节流防止重复点击
const handleLoadMore = throttle(() => {
  if (state.loading || !state.isMore) return;
  state.page.page++;
  debouncedGetArticleList();
}, 1000);

// 分页改变
const handlePageChange = (page: number, size: number, scorllTopCallback: Function) => {
  state.page.page = page;
  state.page.size = size;
  debouncedGetArticleList(scorllTopCallback);
};

// 点击文章详情
const handleArticle = (row: any) => {
  throttledRouterPush('/article/detail', { id: row.id });
};

// 点击阅览
const handleView = (row: any) => {
  throttledRouterPush('/preview', { id: row.id });
};

// 点击文章类型
const handleType = (type: number) => {
  throttledRouterPush('/article/category', { type });
};

// 点击文章分类
const handleCategory = (label: string) => {
  throttledRouterPush('/article/category', { type: 4, category: label });
};

// 重置
const reset = () => {
  state.page.page = 1;
  state.page.total = 0;
  state.articleList = [];
  state.isMore = true;
  debouncedGetArticleList();
};

const showLive2d = () => {
  let type = state.currentModule.moduleTitle;
  const msg = '这里是「' + type + '」类型的文章！';
  live2dShowMessage(msg);
};

// 初始化数据
getArticleListFn();

onMounted(() => {});

onUpdated(() => {
  live2dReloadElement();
  showLive2d();
});

watch(
  () => route.query.type,
  n => {
    if (n != '4') {
      state.currentModule = typeMap[n as ArticleType];
      reset();
    }
  }
);

watch(
  () => route.query.category,
  n => {
    if (route.query.type == '4') {
      state.currentModule = typeMap[route.query.type];
      state.currentModule.moduleTitle = n;
      reset();
    }
  }
);
</script>

<style lang="scss" scoped>
.article-wrap {
  width: 100%;
  height: 100%;
  .article-container-wrap {
    width: 100%;
    height: 100%;
    background-color: var(--bg-image-wrap-color);
    max-width: var(--content-max-width);
    margin: 0 auto;
  }
  .m-article-container-wrap {
    .article-container-inner {
      padding-top: 10px;
      max-width: var(--content-max-width-m);
      margin: 0 auto;
    }
  }

  .no-content {
    height: calc(100vh - 400px);
  }
  .m-no-content {
    height: calc(100vh - 300px);
    padding-top: 0 !important;
  }
}
</style>
